/* Copyright 2017, 2018, 2019, 2020 Gabriel Czernikier
 *
 * This file is part of Côtehaus.
 * Côtehaus is free software: you can redistribute it and/or modify
 * it under the terms of one of the following:
 * 1. The same dual license as that of Côtehaus itself, of which individual licenses are
 * the MIT License and the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 2. The MIT License.
 * 3. The GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * Côtehaus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * MIT License and the GNU General Public License for more details.
 * You should have received a copy of the MIT License and the GNU General Public License
 * along with Côtehaus.  If not, see <https://opensource.org/licenses/MIT> and <http://www.gnu.org/licenses/>.
 */
#define _BSD_SOURCE
#include <endian.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "class_def.h"
#include "composer.h"
#include "lgwseq.h"
#include "types.h"

struct oclass_def_st {
	struct class_def_st *theclass;  // the target class/interface
	int parent_count; // parents chained to this child
}
struct child_stub {
	struct oclass_def_st *oclass_def;
	struct list_head child_list;
}
LIST_HEAD(class_def_head);  // struct class_def_st
unsigned long class_def_list_size = 0;
struct oclass_def_st *oclass_def_ary; // supports both sorted views of class_def_st set, first by full class name and later by parent->child relation
void assert_full_class_name_uniqueness();
int fullname_compar(struct oclass_def_st *a, struct oclass_def_st *b);
int typ_clstyp_compar(struct type_id_st *a, struct oclass_def_st *b);
int parent_exhaustion_compar(struct oclass_def_st *a, struct oclass_def_st *b);

struct class_def_st *add_class_def(type_id *clstyp, bool external) {
	struct class_def_st *new_class_def = malloc(sizeof(struct class_def_st));
	new_class_def->clstyp = clstyp;
	new_class_def->external = external;
	list_add(&new_class_def->clsdef_lst, &class_def_head);
	class_def_list_size++;
}

void build_oclass_def_ary() {
	oclass_def_ary = malloc(class_def_list_size*sizeof(struct oclass_def_st));
	struct class_def_st *tmp;
	unsigned long i=0;
	list_for_each_entry(tmp, struct class_def_st, class_def_list, class_def_head) {
		oclass_def_ary[i].theclass = tmp;
		tmp-> cotehaus_oclass_def=oclass_def_ary[i];
		oclass_def_ary[i].parent_count=0;
		INIT_LIST_HEAD(&oclass_def_ary[i].child_head);
		i++;
	}
	assert_full_class_name_uniqueness();
	/* 
	 * step 1: exist class_def->{parenttyp, [parent_iface]}, registered as [type_id_ptr_client] TODO rework type resolution model.
	 * step 2: class_def->{parenttyp, [parent_iface]}->str_id are valid after type resolution.
	 * step 3: [str_id.idx] are all uniqued by build_ostr_id_and_pack_str_dt. [type_id.idx] are all uniqued by build_otype_id.
	 * step 4: inversion of references class_def->super_type_id and each class_def->super_iface_type_id_set to (super_)class_def->sub_class_def_set.
	 */
	for(struct oclass_def_st *oclass=oclass_def_ary; oclass!=oclass_def_ary+class_def_list_size; oclass++) {
		// subclass linkage
		if(oclass->theclass->parenttyp!=NULL) {
			struct oclass_def_st *oparentcls = oclass->theclass->parenttyp->cotehaus_class_def->cotehaus_oclass_def;
			struct child_stub *child = malloc(sizeof(struct child_stub));
			child->oclass_def = oclass;
			list_add(&child->child_list, &oparentcls->child_head);
			oclass->parent_count++;
		}
		// subinterface linkage
		if(oclass->theclass->parent_iface!=NULL) {
			struct type_member_st *tmp;
			list_foreach_entry(tmp, struct type_member_st, type_member_list,  &oclass->theclass->parent_iface->type_member_head) {
				struct oclass_def_st *oparentcls = tmp->type_id->cotehaus_class_def->cotehaus_oclass_def;
				struct child_stub *child = malloc(sizeof(struct child_stub));
				child->oclass_def = oclass;
				list_add(&child->child_list, &oparentcls->child_head);
				oclass->parent_count++;
			} // list_foreach_entry
		} // if
	} // for
	// step 4 is also where class-name non-duplication validation will take place, given that oclass_def defined therein fits into that purpose.
	struct oclass_def_st *edge=oclass_def_ary;
	while(edge!=oclass_def_ary+class_def_list_size) {
		qsort(edge, oclass_def_ary+class_def_list_size-edge, sizeof(struct oclass_def_st), (int (*)(void*,void*))parent_exhaustion_compar); 
		// assert edge->parent_count==0
		do {
			do {
				struct child_stub *child;
				list_pop_entry(child, struct child_stub, child_list, &edge->child_head);
				if(child==NULL)
					break;
				child->oclass_def->parent_count--;
				free(child);
			} while(1);
			edge++;
		} while(edge!=oclass_def_ary+class_def_list_size && edge->parent_count==0);
	}
}

void assert_full_class_name_uniqueness() {
	// assert on full class name ("pack.age.Cls").
	qsort(oclass_def_ary, oclass_def_ary+class_def_list_size, sizeof(struct oclass_def_st), (int (*)(void*,void*))fullname_compar);
	unsigned long long typidx=oclass_def_ary[0].theclass->clstyp->idx;
	for(i=1;i<class_def_list_size;i++) {
		if(typidx==oclass_def_ary[i].theclass->clstyp->idx) {
			fprintf(stderr, "Full class name uniqueness fail.\n");/*TODO give class name, source file and line number.*/
			exit(-1);
		}
	}

}
int fullname_compar(struct oclass_def_st *a, struct oclass_def_st *b) {
	/* sorting by fullname (the class/reftype full name) involves abstraction about the rest of the path
	 * that brings to class names access and comparission. For the explained in steps 1, 2 and 3 of the build_oclass_def_ary function, only the struct type_id_st.idx is used in its stand.
	 */
	return a->theclass->class_type->idx-b->theclass->class_type->idx;
}

int typ_clstyp_compar(struct type_id_st *a, struct oclass_def_st *b) {
	return a->idx-b->theclass->clstyp->idx;
}

int parent_exhaustion_compar(struct oclass_def_st *a, struct oclass_def_st *b) {
	return !a->parent_count?/*a<=b*/-b->parent_count:/*a>=b*/!b->parent_count;
}

void pack_oclass_def() {
	for(struct oclass_def_st *oclass_def_representative = oclass_def_ary;oclass_def_representative!=NULL; oclass_def_representative=oclass_def_representative->next_representative){
		// skip items collected during parsing of external refs
		if(oclass_def_representative->theclass->external)
			continue;
		uint32_t packed = htole32(oclass_def_representative->theclass->clstyp->idx);
		BUFFER_WRITE(buffer[NH_CLASS_DEF_IDX],buf_len[NH_CLASS_DEF_IDX],&packed,buf_offset[NH_CLASS_DEF_IDX],sizeof(uint32_t))
		// TODO access_flags
		packed = htole32(oclass_def_representative->theclass->parenttyp!=NULL ? oclass_def_representative->theclass->parenttyp->idx : NO_INDEX);
		BUFFER_WRITE(buffer[NH_CLASS_DEF_IDX],buf_len[NH_CLASS_DEF_IDX],&packed,buf_offset[NH_CLASS_DEF_IDX],sizeof(uint32_t))
		packed = htole32(oclass_def_representative->theclass->parent_iface!=NULL ? oclass_def_representative->theclass->parent_iface->relativeoffset + up_bounds_ary[NH_TYPE_LIST_IDX-1] : 0);
		BUFFER_WRITE(buffer[NH_CLASS_DEF_IDX],buf_len[NH_CLASS_DEF_IDX],&packed,buf_offset[NH_CLASS_DEF_IDX],sizeof(uint32_t))
		// TODO rest of the fields
	}

}
